package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.utils.TransactionManager;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;

public class AnnotationBeanFactory implements BeanFactory {
    private HashMap<String, Object> beans = new HashMap<>();
    private HashMap<String, BeanDefinition> relations = new HashMap<>();
    private HashMap<String, Object> earlyBeans = new HashMap<>();
    private TransactionManager transactionManager = new TransactionManager();

    public void registerResolvableDependency(HashMap<String, BeanDefinition> relations) {
        this.relations = relations;
    }

    public void preInstantiateSingletons() {
        relations.values().forEach(this::resolveClass);
    }

    public   Object createProxyObj(Object object, Class cl){
        return   Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    Object result  = null;
                    Method [] methods = cl.getMethods();
                    boolean isTransactional = false;
                    for(Method methodItem: methods){
                        isTransactional = isTransactional||(methodItem.getAnnotation(Transactional.class)!=null&& method.getName().equals(methodItem.getName()));
                    }
                    if(isTransactional){
                        try {
                            System.out.println("Transactional start");
                            transactionManager.beginTransaction();
                            result = method.invoke(object, args);
                            System.out.println("Transactional commit");
                            transactionManager.commit();
                        }catch (Exception e) {

                            // 回滚事务
                            transactionManager.rollback();
                            System.out.println("Transactional rollback");

                        }
                    }else {
                        result = method.invoke(object, args);
                    }

                    return result;
                });
    }

    //TODO: 循环依赖问题未解决
    private void resolveClass(BeanDefinition beanDefinition) {
        if(beans.containsKey(beanDefinition.getClassName())){
            return;
        }
        Class<?> cl = beanDefinition.getCl();
        try {
            Object obj = cl.getConstructor().newInstance();
            Field[] fields = cl.getDeclaredFields();
            for (Field field : fields) {
                Autowired annotation = field.getAnnotation(Autowired.class);
                if (annotation != null) {
                    if(!beans.containsKey(field.getType().getSimpleName())){
                        resolveClass(this.relations.get(annotation.value()));
                    }
                    Method[] methods = cl.getMethods();
                    for(Method method: methods){
                        if(method.getName().equalsIgnoreCase("set"+field.getType().getSimpleName())){
                            method.invoke(obj, beans.get(annotation.value()));
                        }
                    }
                    //为啥直接通过反射这样注入会有问题会有问题
                    //field.setAccessible(true);
                    //field.set(obj, beans.get(((Autowired) annotation).value()));
                }
            }
            beans.put(cl.getSimpleName(), createProxyObj(obj, cl));
        } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String name) {
        return beans.get(name);
    }
}
